import { Controller, Get, Post, Put, Delete, Body, Param, Query, UploadedFile, UseInterceptors, Res } from '@nestjs/common';
import { FileInterceptor } from '@nestjs/platform-express';
import { XLayerService } from '../services/xlayer.service';
import { CreateXLayerTokenDto, ContributeXLayerTokenDto, BuyXLayerTokenDto, SellXLayerTokenDto, PrepareXLayerBuyDto, PrepareXLayerSellDto, PrepareXLayerApproveDto, PrepareXLayerCreateDto, ConfirmXLayerCreateDto, ConfirmXLayerBuyDto } from '../dto/xlayer.dto';

@Controller('api/xlayer')
export class XLayerController {
  constructor(private readonly xlayerService: XLayerService) {}

  // Get X Layer statistics
  @Get('stats')
  async getStats() {
    return await this.xlayerService.getStats();
  }

  // Get all X Layer tokens
  @Get('tokens')
  async getTokens(
    @Query('page') page?: string,
    @Query('limit') limit?: string,
    @Query('search') search?: string,
    @Query('filter') filter?: 'all' | 'active' | 'launched' | 'cancelled',
    @Query('sort') sort?: 'newest' | 'oldest' | 'price_high' | 'price_low' | 'market_cap' | 'volume'
  ) {
    const pageNum = parseInt(page || '1');
    const limitNum = parseInt(limit || '20');
    
    return await this.xlayerService.getTokens({
      page: pageNum,
      limit: limitNum,
      search,
      filter,
      sort
    });
  }

  // Get specific X Layer token
  @Get('tokens/:id')
  async getToken(@Param('id') id: string) {
    return await this.xlayerService.getToken(id);
  }

  // Get token chart data
  @Get('tokens/:id/chart')
  async getTokenChart(
    @Param('id') id: string,
    @Query('timeframe') timeframe: '1h' | '24h' | '7d' | '30d' = '24h'
  ) {
    return await this.xlayerService.getTokenChart(id, timeframe);
  }

  // Create new X Layer token launch
  @Post('tokens')
  async createToken(@Body() createTokenDto: CreateXLayerTokenDto) {
    return await this.xlayerService.createToken(createTokenDto);
  }

  // Prepare create transaction (for wallet signing)
  @Post('tokens/prepare-create')
  async prepareCreate(@Body() dto: PrepareXLayerCreateDto) {
    return await this.xlayerService.prepareCreateTransaction(dto);
  }

  // Confirm create transaction (after wallet signing)
  @Post('tokens/confirm-create')
  async confirmCreate(@Body() dto: ConfirmXLayerCreateDto) {
    return await this.xlayerService.confirmCreateTransaction(dto);
  }

  // Contribute to X Layer token launch
  @Post('tokens/:id/contribute')
  async contribute(
    @Param('id') id: string,
    @Body() contributeDto: ContributeXLayerTokenDto
  ) {
    return await this.xlayerService.contribute(id, contributeDto);
  }

  // Buy tokens (bonding curve)
  @Post('tokens/:launchId/buy')
  async buy(
    @Param('launchId') launchId: string,
    @Body() dto: BuyXLayerTokenDto
  ) {
    dto.launchId = parseInt(launchId, 10);
    return await this.xlayerService.buy(dto);
  }

  // Prepare buy transaction (for wallet signing)
  @Post('tokens/:launchId/prepare-buy')
  async prepareBuy(
    @Param('launchId') launchId: string,
    @Body() dto: PrepareXLayerBuyDto
  ) {
    dto.launchId = parseInt(launchId, 10);
    return await this.xlayerService.prepareBuyTransaction(dto);
  }

  // Confirm buy transaction (record contribution after wallet transaction)
  @Post('tokens/:launchId/confirm-buy')
  async confirmBuy(
    @Param('launchId') launchId: string,
    @Body() dto: ConfirmXLayerBuyDto
  ) {
    dto.launchId = parseInt(launchId, 10);
    return await this.xlayerService.confirmBuyTransaction(dto);
  }

  // Sell tokens
  @Post('tokens/:launchId/sell')
  async sell(
    @Param('launchId') launchId: string,
    @Body() dto: SellXLayerTokenDto
  ) {
    dto.launchId = parseInt(launchId, 10);
    return await this.xlayerService.sell(dto);
  }

  // Prepare sell transaction (for wallet signing)
  @Post('tokens/:launchId/prepare-sell')
  async prepareSell(
    @Param('launchId') launchId: string,
    @Body() dto: PrepareXLayerSellDto
  ) {
    dto.launchId = parseInt(launchId, 10);
    return await this.xlayerService.prepareSellTransaction(dto);
  }

    // Prepare token approval transaction (for selling)
  @Post('tokens/:launchId/prepare-approve')
  async prepareApprove(@Param('launchId') launchId: string, @Body() dto: PrepareXLayerApproveDto) {
    return await this.xlayerService.prepareApprove(parseInt(launchId), dto);
  }

  // Cancel token launch (creator only)
  @Post('tokens/:launchId/cancel')
  async cancelLaunch(@Param('launchId') launchId: string, @Body() dto: { creatorAddress: string; creatorPrivateKey: string }) {
    return await this.xlayerService.cancelLaunch(parseInt(launchId), dto);
  }

  // Prepare cancel launch transaction (for wallet signing)
  @Post('tokens/:launchId/prepare-cancel')
  async prepareCancel(@Param('launchId') launchId: string, @Body() dto: { creatorAddress: string }) {
    return await this.xlayerService.prepareCancel(parseInt(launchId), dto);
  }

  // Claim refund from cancelled token
  @Post('tokens/:launchId/refund')
  async claimRefund(@Param('launchId') launchId: string, @Body() dto: { contributorAddress: string; contributorPrivateKey: string }) {
    return await this.xlayerService.claimRefund(parseInt(launchId), dto);
  }

  // Prepare refund transaction (for wallet signing)
  @Post('tokens/:launchId/prepare-refund')
  async prepareRefund(@Param('launchId') launchId: string, @Body() dto: { contributorAddress: string }) {
    return await this.xlayerService.prepareRefund(parseInt(launchId), dto);
  }

  // Confirm refund transaction (after successful wallet transaction)
  @Post('tokens/:launchId/confirm-refund')
  async confirmRefund(
    @Param('launchId') launchId: string,
    @Body() dto: { contributorAddress: string; txHash: string },
  ) {
    return await this.xlayerService.confirmRefund(parseInt(launchId), dto);
  }

  // Check launch status from contract (for debugging)
  @Get('tokens/:launchId/contract-status')
  async checkContractStatus(@Param('launchId') launchId: string) {
    return await this.xlayerService.checkLaunchStatusFromContract(parseInt(launchId));
  }

  // Test refund call (for debugging)
  @Post('tokens/:launchId/test-refund')
  async testRefund(@Param('launchId') launchId: string, @Body() dto: { userAddress: string }) {
    return await this.xlayerService.testRefundCall(parseInt(launchId), dto.userAddress);
  }

  // Test half refund (for debugging contract balance issues)
  @Post('tokens/:launchId/test-half-refund')
  async testHalfRefund(@Param('launchId') launchId: string, @Body() dto: { userAddress: string }) {
    return await this.xlayerService.testHalfRefund(parseInt(launchId), dto.userAddress);
  }

  // Check token approval status for refund
    @Post('tokens/:launchId/check-approval')
  async checkTokenApproval(
    @Param('launchId') launchId: string,
    @Body() dto: { userAddress: string; tokenAddress: string; spenderAddress: string }
  ) {
    // TODO: Implement checkApproval method in XLayerService
    return { success: false, message: 'Method not implemented yet' };
  }

  // Sync token state with smart contract
  @Post('tokens/:launchId/sync')
  async syncTokenState(@Param('launchId') launchId: string) {
    try {
      const result = await this.xlayerService.syncTokenWithContract(parseInt(launchId));
      return result;
    } catch (error) {
      console.error('Error syncing token:', error);
      throw error;
    }
  }

  // Manual token graduation (for debugging)
  @Post('tokens/:launchId/manual-graduate')
  async manualGraduateToken(
    @Param('launchId') launchId: string,
    @Body() dto?: { creatorPrivateKey?: string }
  ) {
    try {
      const result = await this.xlayerService.manualGraduateToken(
        parseInt(launchId),
        dto?.creatorPrivateKey
      );
      return result;
    } catch (error) {
      console.error('Error in manual graduation:', error);
      throw error;
    }
  }

  // Refresh token price from smart contract
  @Post('tokens/:launchId/refresh-price')
  async refreshTokenPrice(@Param('launchId') launchId: string) {
    try {
      const result = await this.xlayerService.refreshTokenPrice(parseInt(launchId));
      return result;
    } catch (error) {
      console.error('Error refreshing price:', error);
      throw error;
    }
  }

  // Debug token state (compare database vs smart contract)
  @Get('tokens/:launchId/debug')
  async debugTokenState(@Param('launchId') launchId: string) {
    try {
      const result = await this.xlayerService.debugTokenState(parseInt(launchId));
      return result;
    } catch (error) {
      console.error('Error debugging token state:', error);
      throw error;
    }
  }

  // Get user's X Layer tokens
  @Get('users/:address/tokens')
  async getUserTokens(@Param('address') address: string) {
    return await this.xlayerService.getUserTokens(address);
  }

  // Get user's contributions
  @Get('users/:address/contributions')
  async getUserContributions(@Param('address') address: string) {
    return await this.xlayerService.getUserContributions(address);
  }

  // Upload image for X Layer token
  @Post('upload')
  @UseInterceptors(FileInterceptor('image'))
  async uploadImage(@UploadedFile() file: Express.Multer.File) {
    if (!file) {
      throw new Error('No file uploaded');
    }
    
    return await this.xlayerService.uploadImage(file);
  }

  // Update token information (for real-time sync)
  @Put('tokens/:id/sync')
  async syncToken(@Param('id') id: string) {
    return await this.xlayerService.syncTokenFromContract(id);
  }

  // Get token holders
  @Get('tokens/:id/holders')
  async getTokenHolders(@Param('id') id: string) {
    return await this.xlayerService.getTokenHolders(id);
  }

  // Get recent transactions for a token
  @Get('tokens/:id/transactions')
  async getTokenTransactions(
    @Param('id') id: string,
    @Query('limit') limit?: string
  ) {
    const limitNum = parseInt(limit || '50');
    return await this.xlayerService.getTokenTransactions(id, limitNum);
  }

  // Test file access (temporary debug endpoint)
  @Get('test-file/:filename')
  async testFile(@Param('filename') filename: string) {
    const fs = require('fs');
    const path = require('path');
    const filePath = path.join(process.cwd(), 'uploads', 'images', filename);
    
    return {
      filename,
      filePath,
      exists: fs.existsSync(filePath),
      cwd: process.cwd(),
      stat: fs.existsSync(filePath) ? fs.statSync(filePath) : null
    };
  }

  // Serve image files directly
  @Get('images/:filename')
  async serveImage(@Param('filename') filename: string, @Res() res: any) {
    const fs = require('fs');
    const path = require('path');
    const filePath = path.join(process.cwd(), 'uploads', 'images', filename);
    
    if (!fs.existsSync(filePath)) {
      return res.status(404).json({ error: 'File not found' });
    }

    const stat = fs.statSync(filePath);
    const mimeType = filename.toLowerCase().endsWith('.png') ? 'image/png' : 'image/jpeg';
    
    res.setHeader('Content-Type', mimeType);
    res.setHeader('Content-Length', stat.size);
    res.setHeader('Cache-Control', 'public, max-age=31536000');
    
    const stream = fs.createReadStream(filePath);
    stream.pipe(res);
  }
}